package com.yky.dao.base;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.yky.entity.base.QueryResult;
import com.yky.utils.base.GenericsUtils;

/**
 * DAO层封装使用了泛型，包含常用的CURD和分页操作
 * 
 */
@SuppressWarnings("unchecked")
public abstract class BaseDaoSupport<T> extends HibernateDaoSupport implements
		IBaseDao<T> {
	protected Class<T> entityClass = GenericsUtils.getSuperClassGenricType(this
			.getClass());
	protected String entityClassName = getEntityName(this.entityClass);
	protected String keyFieldName = getKeyFieldName(this.entityClass);

	private static Log log = LogFactory.getLog(BaseDaoSupport.class);

	// 为父类HibernateDaoSupport注入sessionFactory的值
	@Resource(name = "sessionFactory")
	public void setSuperSessionFactory(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	/**
	 * 多条件查询
	 */
	public List findByPropertys(final String wherejpql,
			final Object[] queryParams) {
		
		return findByPropertys(-1, -1, wherejpql, queryParams, null);
	}

	/**
	 * 多条件带排序查询
	 */
	public List findByPropertys(final String wherejpql,
			final Object[] queryParams,
			final LinkedHashMap<String, String> orderby) {
		return findByPropertys(-1, -1, wherejpql, queryParams, orderby);
	}

	/**
	 * 多条件-排序-起始页-结束页
	 * 
	 * @param firstindex
	 * @param maxresult
	 * @param wherejpql
	 * @param queryParams
	 * @param orderby
	 * @return
	 */
	public List findByPropertys(final int firstindex, final int maxresult,
			final String wherejpql, final Object[] queryParams,
			final LinkedHashMap<String, String> orderby) {
		return (List) super.getHibernateTemplate().execute(
				new HibernateCallback<T>() {
					public T doInHibernate(Session session)
							throws HibernateException, SQLException {
						String hql = "from "
								+ entityClassName
								+ " o "
								+ (wherejpql == null
										|| "".equals(wherejpql.trim()) ? ""
										: " where " + wherejpql)
								+ buildOrderby(orderby);
						Query query = session.createQuery(hql);
						setQueryParams(query, queryParams);// where
						if (firstindex != -1 && maxresult != -1)
							query.setFirstResult(firstindex).setMaxResults(
									maxresult);// last
						//System.out.println("后台打印HQL:"+hql);
						//System.out.println("后台类名称:"+entityClassName);
						return (T) query.list();
					}
				});
	}

	/**
	 * 原生sql查询
	 * 
	 * @param hql
	 * @return
	 */
	public List findBySQL(final String sql) {
		return getHibernateTemplate().executeFind(new HibernateCallback<T>() {
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				//System.out.println("后台打印HQL:"+sql);
				return (T) session.createSQLQuery(sql).list();
			}
		});
	}

	/*
	 * from查询
	 * 
	 * @see org.usc.daos.DAO#findByProperty(java.lang.String, java.lang.Object)
	 */
	public List<T> findByProperty(String propertyName, Object value) {
		String queryString = "from " + entityClassName + " o where o."
				+ propertyName + "= ?";
		return super.getHibernateTemplate().find(queryString, value);
	}

	/**
	 * like 查询
	 */
	public List<T> findEntityListByLike(String propertyName, Object value) {
		// TODO Auto-generated method stub
		String queryString = "from " + entityClassName + " o where o."
				+ propertyName + " like ?";
		return super.getHibernateTemplate()
				.find(queryString, "%" + value + "%");
	}

	public List findByPropertyOrderByStrAndRownum(String propertyName,
			final Object value, Object sortValue, Object sortDesc,
			final Integer rownum) {

		final String queryString = "from " + entityClassName + " o where o."
				+ propertyName + "= ? order by o." + sortValue + " " + sortDesc;

		try {
			if (rownum != null && rownum > 0) {
				return getHibernateTemplate().executeFind(
						new HibernateCallback<T>() {
							public T doInHibernate(Session session)
									throws HibernateException, SQLException {
								Query query = session.createQuery(queryString);
								query.setParameter(0, value);
								query.setMaxResults(rownum);
								return (T) query.list();
							}
						});
			} else
				return getHibernateTemplate().find(queryString,
						"%" + value + "%");
		} catch (RuntimeException re) {
			log.error("find " + queryString, re);
			throw re;
		}

	}

	/**
	 * 根据hql查询,直接使用HibernateTemplate的find函数. 取有限条数
	 */
	public List findByPropertyOrderByAndRownum(String propertyName,
			final Object value, Object sortValue, final Integer rownum) {
		return findByPropertyOrderByStrAndRownum(propertyName, value,
				sortValue, null, rownum);
	}

	/**
	 * 根据hql查询,直接使用HibernateTemplate的find函数.
	 * 
	 * @param hql
	 * @param rownum:
	 *            取有限条数
	 * @param values
	 * @return
	 */
	public List find(final String hql, final Integer rownum) {
		try {
			if (rownum != null && rownum > 0) {
				return getHibernateTemplate().executeFind(
						new HibernateCallback<T>() {
							public T doInHibernate(Session session)
									throws HibernateException, SQLException {
								Query query = session.createQuery(hql);
								query.setMaxResults(rownum);
								return (T) query.list();
							}
						});
			} else
				return getHibernateTemplate().find(hql);
		} catch (RuntimeException re) {
			log.error("find " + hql, re);
			throw re;
		}
	}

	/**
	 * 查询某个类的list
	 * 
	 * @return
	 */
	public List<T> findEntityList() {
		return getHibernateTemplate().executeFind(new HibernateCallback<T>() {
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				String hql = "from " + entityClassName + " ";
				Query query = session.createQuery(hql);
				return (T) query.list();
			}
		});
		// String hql = "from " + entityClassName + " ";
		// try {
		// Query query = this.getSession().createQuery(hql);
		//			
		// } catch (RuntimeException re) {
		// log.error("find list " + hql, re);
		// throw re;
		// }
	}

	/**
	 * 根据属性名和属性值查询唯一对象.!!!
	 * 
	 * @return 符合条件的唯一对象 or null if not found.
	 */
	public <T> T findUniqueBy(String propertyName, Object value) {
		T t = null;
		try {
			Assert.hasText(propertyName);
			t = (T) createCriteria(entityClass,
					Restrictions.eq(propertyName, value)).uniqueResult();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return t;
	}

	private <T> Criteria createCriteria(Class<T> entityClass,
			Criterion... criterions) {
		Criteria criteria = null;
		try {
			criteria = getSession().createCriteria(entityClass);
			for (Criterion c : criterions) {
				criteria.add(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return criteria;
	}

	/**
	 * 创建Query对象.
	 * 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置.
	 * 留意可以连续设置,如下：
	 * 
	 * <pre>
	 * dao.getQuery(hql).setMaxResult(100).setCacheable(true).list();
	 * </pre>
	 * 
	 * 调用方式如下：
	 * 
	 * <pre>
	 *          dao.createQuery(hql)
	 *          dao.createQuery(hql,arg0);
	 *          dao.createQuery(hql,arg0,arg1);
	 *          dao.createQuery(hql,new Object[arg0,arg1,arg2])
	 * </pre>
	 * 
	 * @param values
	 *            可变参数.
	 */
	public Query createQuery(String hql, Object... values) {
		Query query = null;
		try {
			Assert.hasText(hql);
			query = getSession().createQuery(hql);
			if (values != null) {
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return query;
	}

	/**
	 * flush
	 * 
	 */
	public void flush() {
		try {
			getHibernateTemplate().flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * clear
	 */
	public void clear() {
		try {
			getHibernateTemplate().clear();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 删除对象.
	 */
	public void remove(Object o) {
		try {
			getHibernateTemplate().delete(o);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * @see org.usc.daos.DAO#delete(java.io.Serializable[])
	 */
	public void delete(Serializable... entityids) {
		for (Object id : entityids) {
			remove(find((Serializable) id));
		}
	}

	/*
	 * @see org.usc.daos.DAO#find(java.io.Serializable)
	 */
	public T find(Serializable entityId) {
		if (null != entityId)
			return (T) super.getHibernateTemplate().get(entityClass, entityId);
		return null;
	}

	/**
	 * 获取全部对象.
	 */
	public <T> List<T> getAll() {
		List<T> list = null;
		try {
			list = (List<T>) getHibernateTemplate().loadAll(entityClass);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * 获取全部对象,带排序字段与升降序参数.
	 */
	public <T> List<T> getAll(String orderBy, boolean isAsc) {
		List<T> list = null;
		try {
			Assert.hasText(orderBy);
			if (isAsc) {
				list = getHibernateTemplate().findByCriteria(
						DetachedCriteria.forClass(entityClass).addOrder(
								Order.asc(orderBy)));
			} else {
				list = getHibernateTemplate().findByCriteria(
						DetachedCriteria.forClass(entityClass).addOrder(
								Order.desc(orderBy)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	public <T> T merge() {
		T t = null;
		log.debug("merging " + entityClass.getClass().getName() + " instance");
		try {
			t = (T) getHibernateTemplate().merge(entityClass);
			log.debug("merge successful");
			// return t;
		} catch (Exception e) {
			log.error("merge failed", e);
			e.printStackTrace();
		}
		return t;
	}

	/*
	 * @see org.usc.daos.DAO#getCount()
	 */
	public int getCount() {
		String hql = "select count( " + keyFieldName + ") from "
				+ entityClassName;
		int count = Integer.parseInt(super.getHibernateTemplate().find(hql)
				.get(0).toString());
		return count;
	}

	public void save(Object entity) {
		try {
			super.getHibernateTemplate().save(entity);
			super.getHibernateTemplate().flush();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

	/*
	 * @see org.usc.daos.DAO#update(java.lang.Object)
	 */
	public void update(Object entity) {
		try {
			super.getHibernateTemplate().update(entity);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	public void saveOrUpdate(Object entity) {
		try {
			getHibernateTemplate().saveOrUpdate(entity);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * @see org.usc.daos.DAO#getScrollData(int, int, java.lang.String,
	 *      java.lang.Object[], java.util.LinkedHashMap)
	 */
	public QueryResult<T> getScrollData(final int firstindex,
			final int maxresult, final String wherejpql,
			final Object[] queryParams,
			final LinkedHashMap<String, String> orderby) {
		final QueryResult<T> queryResult = new QueryResult<T>();

		super.getHibernateTemplate().execute(new HibernateCallback<T>() {
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				String hql = "from "
						+ entityClassName
						+ " o "
						+ (wherejpql == null || "".equals(wherejpql.trim()) ? ""
								: " where " + wherejpql)
						+ buildOrderby(orderby);
				Query query = session.createQuery(hql);
				setQueryParams(query, queryParams);// where
				queryResult.setTotalRecord(query.list().size());// first get
				// size

				if (firstindex != -1 && maxresult != -1)
					query.setFirstResult(firstindex).setMaxResults(maxresult);// last
				// page

				queryResult.setResultList(query.list());

				return null;
			}

		});

		return queryResult;

	}

	/*
	 * @see org.usc.daos.DAO#getScrollData(int, int, java.lang.String,
	 *      java.lang.Object[])
	 */
	public QueryResult<T> getScrollData(int firstindex, int maxresult,
			String wherejpql, Object[] queryParams) {
		return getScrollData(firstindex, maxresult, wherejpql, queryParams,
				null);
	}

	/*
	 * @see org.usc.daos.DAO#getScrollData(int, int, java.util.LinkedHashMap)
	 */
	public QueryResult<T> getScrollData(final int firstindex,
			final int maxresult, final LinkedHashMap<String, String> orderby) {
		return getScrollData(firstindex, maxresult, null, null, orderby);

	}

	/*
	 * @see org.usc.daos.DAO#getScrollData(int, int)
	 */
	public QueryResult<T> getScrollData(final int firstindex,
			final int maxresult) {
		return getScrollData(firstindex, maxresult, null, null, null);
	}

	/*
	 * @see org.usc.daos.DAO#getScrollData()
	 */
	public QueryResult<T> getScrollData() {
		return getScrollData(-1, -1, null, null, null);
	}

	/*
	 * @see org.usc.daos.DAO#save(java.lang.Object)
	 */

	/**
	 * 获取实体的名称
	 * 
	 * @param <E>
	 * @param clazz
	 *            实体类
	 * @return
	 */
	public static <T> String getEntityName(Class<T> clazz) {
		String entityname = clazz.getSimpleName();
		Entity entity = clazz.getAnnotation(Entity.class);
		if (entity.name() != null && !"".equals(entity.name())) {
			entityname = entity.name();
		}
		return entityname;
	}

	/**
	 * 获取实体的主键
	 * 
	 * @param <E>
	 * @param clazz
	 *            实体类
	 * @return 主键名
	 */
	public static <E> String getKeyFieldName(Class<E> clazz) {
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector
					.getBeanInfo(clazz).getPropertyDescriptors();
			for (PropertyDescriptor propertydesc : propertyDescriptors) {
				Method method = propertydesc.getReadMethod();
				if (null != method
						&& null != method
								.getAnnotation(javax.persistence.Id.class)) {
					return propertydesc.getName();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "id";
	}

	/**
	 * 设置HQL里边的属性值
	 * 
	 * @param query
	 * @param queryParams
	 */
	public static void setQueryParams(Query query, Object[] queryParams) {
		if (queryParams != null && queryParams.length > 0) {
			for (int i = 0; i < queryParams.length; i++) {
				query.setParameter(i, queryParams[i]);// 从0开始
			}
		}
	}

	/**
	 * 组装order by语句
	 * 
	 * @param orderby
	 * @return
	 */
	public static String buildOrderby(LinkedHashMap<String, String> orderby) {
		StringBuffer orderbyql = new StringBuffer("");
		if (orderby != null && orderby.size() > 0) {
			orderbyql.append(" order by ");
			for (String key : orderby.keySet()) {
				orderbyql.append("o.").append(key).append(" ").append(
						orderby.get(key)).append(",");
			}
			orderbyql.deleteCharAt(orderbyql.length() - 1);
		}
		return orderbyql.toString();
	}

	/**
	 * 获取字段数
	 * 
	 * @param <E>
	 * @param clazz
	 * @return
	 */
	public static <E> String getCountField(Class<E> clazz) {
		String out = "o";
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector
					.getBeanInfo(clazz).getPropertyDescriptors();
			for (PropertyDescriptor propertydesc : propertyDescriptors) {
				Method method = propertydesc.getReadMethod();
				if (method != null
						&& method.isAnnotationPresent(EmbeddedId.class)) {
					PropertyDescriptor[] ps = Introspector.getBeanInfo(
							propertydesc.getPropertyType())
							.getPropertyDescriptors();
					out = "o."
							+ propertydesc.getName()
							+ "."
							+ (!ps[1].getName().equals("class") ? ps[1]
									.getName() : ps[0].getName());
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return out;
	}

	/**
	 * 创建Criteria对象 List集合中的数组元素包含三个子元素（依次为字段名、检索值和匹配条件）
	 * 匹配条件:min;max;like;exactitude;order如有其它需求可再添加
	 * 
	 * @deprecated
	 * @param <T>
	 * @param entityClass
	 * @param reasons
	 * @return
	 */
	public <T> Criteria createCriteria(List reasons) {
		Criteria criteria = null;
		try {
			criteria = getSession().createCriteria(entityClass);
			if (reasons != null && reasons.size() > 0)
				for (int i = 0; i < reasons.size(); i++) {
					Object[] objects = (Object[]) reasons.get(i);
					Criterion criterion = null;
					if (objects.length == 2) { // List集合中的数组元素包含两个子元素（依次为字段名和检索值）
						criterion = Expression.eq((String) objects[0],
								objects[1]);
					} else { // List集合中的数组元素包含三个子元素（依次为字段名、检索值和匹配条件）
						String type = (String) objects[2];
						if (type.equals("min")) { // 匹配条件为检索大于检索值的记录
							criterion = Expression.ge((String) objects[0],
									objects[1]);
						} else if (type.equals("max")) { // 匹配条件为检索小于检索值的记录
							criterion = Expression.le((String) objects[0],
									objects[1]);
						} else if (type.equals("like")) { // 匹配条件为模糊查询
							criterion = Expression.like((String) objects[0],
									"%" + objects[1] + "%");
						} else if (type.equals("exactitude")) { // 匹配条件为精确查询
							criterion = Expression.eq((String) objects[0],
									objects[1]);
						} else if (type.equals("order")) { // 对检索结果排序
							String order = (String) objects[1];
							if (order.equals("asc")) {
								criteria.addOrder(Order
										.asc((String) objects[0]));
							} else {
								criteria.addOrder(Order
										.desc((String) objects[0]));
							}
						}
					}
					if (criterion != null)
						criteria.add(criterion);
				}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return criteria;
	}

	public List<T> findByPropertyOrderBy(String propertyName, Object id,
			Object sortValue) {
		// TODO Auto-generated method stub

		String queryString = "from " + entityClassName + " o where o."
				+ propertyName + "= ? order by o." + sortValue;
		return super.getHibernateTemplate().find(queryString, id);
	}

}
